import {FileTransfer, FileTransferObject, FileUploadOptions} from '@ionic-native/file-transfer';
import {File} from '@ionic-native/file';
import {Observable} from "rxjs/Observable";
import {Injectable} from "@angular/core";
import {BASEURL} from "../../../config/url.config";
import {HttpClient} from "@angular/common/http";
import {Platform} from 'ionic-angular';
import {LocalStorageService} from "../local-storage/local-storage.service";

@Injectable()
export class FileService {
    private fileTransfer: FileTransferObject;
    private serverUrl: string = BASEURL;
    private servicePath: string = `${this.serverUrl}/azure/v1/containerSas`;

    constructor(private transfer: FileTransfer,
                private file: File,
                private http: HttpClient,
                private storage: LocalStorageService,
                private platform: Platform) {
        this.fileTransfer = this.transfer.create();
    }

    /**
     *
     * @param {string} fileName
     * @param {string} result
     * @returns {string}
     */
    getAzureServerUrl(fileName: string, sas: string, container: string): string {
        return container + "/" + fileName + "?" + sas;
    }

    /**
     *
     * @returns {string}
     */
    getMimeType(fileName: string): string {
        let extension = this.getExtension(fileName).toLowerCase();
        let mimeType: string = "";
        if (this.platform.is('ios')) {
            switch (extension) {
                case '.amr':
                    mimeType = "audio/mp4a-latm";
                    break;
                case '.png':
                    mimeType = "image/png";
                    break;
                case '.jpg':
                case '.jpeg':
                    mimeType = "image/jpeg";
                    break;
                case '.pdf':
                    mimeType = "application/pdf";
                    break;
                case '.mp4':
                case '.mpg4':
                    mimeType = "video/mp4";
                    break;
            }
            return mimeType;
        }
        else {
            switch (extension) {
                case '.amr':
                    mimeType = "audio/amr";
                    break;
                case '.png':
                    mimeType = "image/png";
                    break;
                case '.jpg':
                case '.jpeg':
                    mimeType = "image/jpeg";
                    break;
                case '.pdf':
                    mimeType = "application/pdf";
                    break;
                case '.mp4':
                case '.mpg4':
                    mimeType = "video/mp4";
                    break;
            }
            return mimeType;
        }
    }

    /**
     * 保存文件
     * @param {string} fileUrl 格式如：file:///storage/emulated/0/test.png
     * @param {string} realName 真实名字
     * @param {string} newName Guid格式，避免在微软云上重复
     * @param {string} mimeType
     * @returns {Observable<any>}
     */
    saveImageFile(fileUrl: string, realName: string, newName: string, mimeType: string): Observable<any> {
        return Observable.create(async observer => {
            let serverUrl = await this.asyncgetFilePath(newName);
            serverUrl['error']&&observer.error(serverUrl);//抛出错误异常
            let dataDir = fileUrl.substring(0, fileUrl.lastIndexOf('/') + 1);
            if (!dataDir.startsWith('file://')) {
                dataDir = "file://" + dataDir;
            }
            let fileStr = await this.readAsArrayBuffer(dataDir,realName);
            fileStr['error']&&observer.error(fileStr);//抛出错误异常
            let options: FileUploadOptions = {
                    fileName: realName,
                    mimeType: mimeType,
                    httpMethod: "PUT",
                    chunkedMode: false,
                    headers: {
                        'x-ms-blob-type': 'BlockBlob',
                        'Content-Length': fileStr['byteLength'],
                        'Content-Type': mimeType
                    }
            }
            await this.uploadFile(fileUrl,serverUrl.toString(),options);
            observer.next(newName);
            observer.complete();
        });
    }

    /**
     * 保存文件
     * @param {string} fileUrl 格式如：file:///storage/emulated/0/test.png
     * @param {string} fileName
     * @param {string} mimeType
     * @returns {Observable<any>}
     */
        saveFile(fileUrl: string, fileName: string, mimeType: string): Observable<any> {
            return Observable.create(async observer => {
                let serverUrl = await this.asyncgetFilePath(fileName);
                let dataDir = this.file.externalRootDirectory;
                if (this.platform.is('ios')) {
                    dataDir = this.file.documentsDirectory;
                }
                if (fileUrl != (dataDir + fileName)) {//文件不存在于根路径，比如在根路径下的Picture或其他路径
                    dataDir = fileUrl.substring(0, fileUrl.lastIndexOf('/') + 1);
                    if (!dataDir.startsWith('file://')) {
                        dataDir = "file://" + dataDir;
                    }
                }
                let fileStr = await this.readAsArrayBuffer(dataDir,fileName);
                let options: FileUploadOptions = {
                        fileName: fileName,
                        mimeType: mimeType,
                        httpMethod: "PUT",
                        chunkedMode: false,
                        headers: {
                            'x-ms-blob-type': 'BlockBlob',
                            'Content-Length': fileStr['byteLength'],
                            'Content-Type': mimeType
                        }
                };
                await this.uploadFile(fileUrl, serverUrl.toString(), options);//上传文件到azure
                let cacheFileUrl =  await this.moveFileToCache(dataDir,fileName);//移动文件到缓存
                // if (this.platform.is('ios')) { //ios情况下需处理
                //     cacheFileUrl = fileName;
                // }
                await this.setDataToStorage(fileName,cacheFileUrl,mimeType);//缓存文件
                observer.next(fileName);
                observer.complete();
            })
        }
    /**
     * 删除文件
     * @param {string} fileId
     * @returns {Observable<any>}
     */
    deleteFile(fileName: string): Observable<any> {
        return Observable.create(async observer => {
            let serverUrl = await this.asyncgetFilePath(fileName);
            // let dataDir = this.file.dataDirectory;
            // if (this.platform.is('ios')) {
            //     dataDir = this.file.documentsDirectory;
            // }
            if (!await this.checkDir()) {
                await this.createDir();
            }
            let dataDir = this.file.externalCacheDirectory+'cache/';
            if (this.platform.is('ios')) { // 兼容IOS
                dataDir = this.file.cacheDirectory+'cache/';
            }
            this.http.delete(serverUrl.toString(), {responseType: 'text'}).subscribe((res) => {
                this.file.removeDir(dataDir, fileName).then();
                this.storage.deleteData(fileName).subscribe();
                observer.next();
                observer.complete();
            }, error => {
                this.file.removeDir(dataDir, fileName).then();
                this.storage.deleteData(fileName).subscribe();
                observer.error(error);
            });
        });
    }

    /**
     * 获取文件
     * @param {string} fileName
     * @returns {Observable<any>}
     */
    getFile(fileName: string, realFileName: string): Observable<any> {
        return Observable.create(async observer => {
            if (!await this.checkDir()) {
                await this.createDir();
            }
            // let dataDir = this.file.cacheDirectory+'cache/';
            let dataDir = this.file.externalCacheDirectory+'cache/';
            if (this.platform.is('ios')) { // 兼容IOS
                dataDir = this.file.cacheDirectory+'cache/';
            }
            let fName = fileName.substring(0, fileName.indexOf('.'));
            let localStorageResult = await this.getDataFromStorage(fName);
            if (localStorageResult) {
                let curFileUrl = localStorageResult['fileUrl'];
                let entry = await this.resolveFileFromLocal(curFileUrl);
                if (entry['isFile']) {
                   //存在本地缓存直接返回
                   observer.next({
                       mimeType: localStorageResult['mimeType'],
                       fileUrl: curFileUrl
                   });
                   observer.complete();
                } else {
                    //不存在本地缓存文件，从azure服务器下载
                    let serverUrl = await this.asyncgetFilePath(fileName);
                    let downloadFilePath = `${dataDir}/${fileName}`;
                    this.fileTransfer.download(serverUrl.toString(), downloadFilePath).then(downloadResult => {
                        observer.next({
                            mimeType: this.getMimeType(fileName),
                            fileUrl: downloadResult.toURL()
                        });
                        observer.complete();
                        }).catch(error => {
                            this.file.removeDir(dataDir, fileName).then();
                            this.storage.deleteData(fileName).subscribe();
                            observer.error(error);
                        });
                }
            } else {
                this.downloadAndCache(fileName, realFileName, observer, dataDir);
            }
        })
    }


    /**
     * 从azure下载并缓存
     * @param fileId
     * @param fileInfoUrl
     * @param downloadUrl
     * @param observer
     */
    async downloadAndCache(fileName, realFileName, observer, dataDir) {
        //不存在本地缓存，从服务器下载，然后缓存
        let serverUrl = await this.asyncgetFilePath(fileName);
        let fName = fileName.substring(0, fileName.indexOf('.'));
            let downloadFilePath = `${dataDir}${fName}/${realFileName}`;
            let cacheFileUrl = await this.downloadFile(serverUrl.toString(), downloadFilePath);
            // if (this.platform.is('ios')) {
            //     cacheFileUrl = `${fName}/${realFileName}`;
            // }

            //缓存文件
            let mimeType = this.getMimeType(fileName);
            await this.setDataToStorage(fName,cacheFileUrl,mimeType);
            observer.next({
                mimeType: mimeType,
                fileUrl: cacheFileUrl
            });
            observer.complete();
    }

    /**
     * 获取文件在azure上的地址，加载图片或预览文件时使用，
     * @param {string} fileName 文件名
     * @returns {string}
     */
    getFilePath(fileName): Observable<any> {
        return Observable.create(observer => {
            let servicePath = this.servicePath;
            this.http.get(servicePath, {responseType: 'text'}).subscribe(data => {
                let resultObj = JSON.parse(data);
                let container = resultObj['container'];
                let sas = resultObj['sas'];
                let curSasUrl = this.getAzureServerUrl(fileName, sas, container);
                observer.next({
                    sasUrl: curSasUrl
                });
                observer.complete();
            }, error => {
                observer.error(error);
            });
        });
    }
    asyncgetFilePath(fileName){
        return new Promise((resolve,reject)=>{
            this.http.get(this.servicePath, {responseType: 'text'}).subscribe(data => {
                let resultObj = JSON.parse(data);
                let container = resultObj['container'];
                let sas = resultObj['sas'];
                let curSasUrl = this.getAzureServerUrl(fileName, sas, container);
                resolve(curSasUrl);
            },error2 => {
                resolve(error2);
            })
        })
    }
    /**
     * 获取azure的信息，包括container和sas
     * @returns {Observable}
     */
    getAzureInfo(): Observable<any> {
        return Observable.create(observer => {
            this.http.get(this.servicePath, {responseType: 'text'}).subscribe(data => {
                observer.next({
                    azureInfo: data
                });
                observer.complete();
            }, error => {
                observer.error(error);
            });
        });
    }

    /**
     * 已知azure的信息后再转换为完整的路径
     * @param {string} azureInfo azure信息,由getAzureInfo()返回
     * @param {string} fileName 文件名
     * @returns {string}
     */
    getCompletePath(fileName: string, azureInfo: string): string {
        let azure = JSON.parse(azureInfo);
        return this.getAzureServerUrl(fileName, azure['sas'], azure['container']);

    }

    /**
     * 下载进度监听事件
     * @param {(event: ProgressEvent) => any} listener
     */
    onProgress(listener: (event: ProgressEvent) => any): void {
        this.fileTransfer.onProgress(listener);
    }

    /**
     * 终止操作
     */
    abort() {
        this.fileTransfer.abort();
    }

    /**
     * 获取扩展名
     * @param {string} fileName
     * @returns {string}
     */
    private getExtension(fileName: string) {
        return fileName.substring(fileName.lastIndexOf('.'));
    }

    /**
     * 下载并安装
     * @param {string} filePath 文件地址
     * @returns {string}
     */
    async downloadFileAndInstall(filePath: string, fileName: string) {
        if(!await this.checkDir()){
            await this.createDir();
        }
        let dataDir = this.file.externalCacheDirectory + 'cache/';
        if (this.platform.is('ios')) { // 兼容IOS
            dataDir = this.file.cacheDirectory+'cache/';
        }
        let downloadFilePath = dataDir + fileName;
        let fileUrl = await this.downloadFile(filePath,downloadFilePath);
        return fileUrl;
    }

    ///////更改为promise方式////////


    downloadFile(filePath, downloadFilePath){
        return new Promise((resolve,reject)=>{
            this.fileTransfer.download(filePath, downloadFilePath).then(downloadResult => {
                resolve(downloadResult.toURL());
            }).catch(error=>{
                reject(error);
            });
        })
    }

    async checkDir() {
        return await this.file.checkDir(this.file.cacheDirectory, 'cache')
            .then((res) => {
                return res;
            }).catch((err) => {
                console.error(err);
            });
    }

    async createDir() {
        return await this.file.createDir(this.file.cacheDirectory, 'cache', false)
            .then((res) => {
                return res;
            }).catch((err) => {
                console.error(err);
            });
    }

    async readAsArrayBuffer(dataDir, fileName){
        return new Promise((resolve,reject)=>{
            this.file.readAsArrayBuffer(dataDir, fileName).then(fileStr => {
                    resolve(fileStr);
            }).catch(error=>{
                reject(error);
            });
        });
    }
    //移动文件到缓存
    async moveFileToCache(dataDir,fileName){
        if (!await this.checkDir()) {
            await this.createDir();
        }
        let cacheDir = this.file.externalCacheDirectory + 'cache/';
        if (this.platform.is('ios')) { // 兼容IOS
            cacheDir = this.file.cacheDirectory+'cache/';
        }
        return new Promise( (resolve, reject) => {
                this.file.moveFile(dataDir,fileName,cacheDir,fileName)
                    .then(res=>{
                        resolve(res.toURL());
                    })
                    .catch(error=>{
                        reject();
                    });
        });
    }
    //上传文件
    uploadFile(fileUrl,serverUrl,options){
        return new Promise((resolve,reject)=>{
            this.fileTransfer.upload(fileUrl, serverUrl, options)
                .then(res=>{
                    resolve();
                })
                .catch(error=>{
                    reject();
                });
        });
    }
    setDataToStorage(fileName,filePath,mimeType){
        return new Promise((resolve,reject)=>{
            this.storage.setData(fileName, {
                mimeType: mimeType,
                fileUrl: filePath
            }).subscribe(data=>{
                resolve();
            },error2 => {
                reject(error2);
            })
        });
    }
    getDataFromStorage(fName){
        return new Promise((resolve,reject)=>{
            this.storage.getData(fName).subscribe(localStorageResult => {
                resolve(localStorageResult);
            },error2 => {
                reject(error2);
            });
        });
    }
    resolveFileFromLocal(fileUrl){
        return new Promise((resolve,reject)=>{
            this.file.resolveLocalFilesystemUrl(fileUrl).then(async entry => {
                resolve(entry);
            }).catch(error=>{
               reject(error);
            });
        });
    }
}
